﻿namespace PortableRiotGames.Fluent
{
    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    using PortableRiotGames.Infrastructure;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Threading.Tasks;

    /// <summary>
    /// Base request builder class.
    /// </summary>
    /// <typeparam name="TRequest">Type of request.</typeparam>
    /// <typeparam name="TResponse">Type of response.</typeparam>
    /// <typeparam name="TBuilder">Type of builder.</typeparam>
    public abstract class RiotGamesBaseBuilder<TRequest, TResponse, TBuilder>
        where TRequest : RequestBase
        where TResponse : ResponseBase, new()
        where TBuilder : RiotGamesBaseBuilder<TRequest, TResponse, TBuilder>
    {
        private List<Region> _regions;
        private TRequest _request;

        public RiotGamesBaseBuilder(TRequest request)
        {
            _request = request;
        }

        protected TRequest Request
        {
            get { return _request; }
        }

        /// <summary>
        /// Gets the response object of this request.
        /// </summary>
        /// <returns>Response object.</returns>
        public virtual RiotGamesAggregateResponse<TResponse> GetResponse()
        {
            return this.GetResponseAsync().Result;
        }

        /// <summary>
        /// Gets the response object of this request asynchronously.
        /// </summary>
        /// <returns>Response object.</returns>
        public virtual async Task<RiotGamesAggregateResponse<TResponse>> GetResponseAsync()
        {
            var aggregateResponse = new RiotGamesAggregateResponse<TResponse>();
            var regions = _regions ?? new List<Region>() { RiotGamesWebAPI.DefaultRegion };

            foreach (var region in regions)
            {
                this.Request.Region = region;

                try
                {
                    if (this.Request.SupportedRegions.Contains(region))
                    {
                        string json = await this.GetResponseStringAsync();
                        var settings = new JsonSerializerSettings()
                        {
                            ContractResolver = new CamelCasePropertyNamesContractResolver()
                        };
                        var response = JsonConvert.DeserializeObject<TResponse>(json, settings);
                        response.RequestURI = this.Request.GetUri();
                        response.HttpStatus = HttpStatus.OK;
                        aggregateResponse.Responses.Add(response);
                    }
                    else
                    {
                        throw new NotSupportedException("Region not supported by Riot Games API for this method.");
                    }
                }
                catch (Exception ex)
                {
                    aggregateResponse.Responses.Add(ResolveException(ex, region));
                }
            }

            return aggregateResponse;
        }

        private TResponse ResolveException(Exception ex, Region region)
        {
            var response = new TResponse() { RequestURI = this.Request.GetUri(), Region = region };

            if (ex.GetType() == typeof(NotSupportedException))
            {
                response.HttpStatus = HttpStatus.BadRequest;
                response.ErrorMessage = ex.Message;
                return response;
            }

            if (ex.Message.Contains("400"))
            {
                response.HttpStatus = HttpStatus.BadRequest;
            }
            else if (ex.Message.Contains("404"))
            {
                response.HttpStatus = HttpStatus.NotFound;
            }
            else if (ex.Message.Contains("500"))
            {
                response.HttpStatus = HttpStatus.InternalServerError;
            }
            else
            {
                throw new Exception("Fatal error occurred.");
            }

            response.ErrorMessage = ex.Message;
            return response;
        }

        /// <summary>
        /// Gets the response string of this request.
        /// </summary>
        /// <returns>Response string.</returns>
        public string GetResponseString()
        {
            return RequestHandler.Request(this.Request.GetUri());
        }

        /// <summary>
        /// Gets the response string of this request asynchronously.
        /// </summary>
        /// <returns>Response string.</returns>
        public async Task<string> GetResponseStringAsync()
        {
            return await RequestHandler.RequestAsync(this.Request.GetUri());
        }

        /// <summary>
        /// Set the regions to send request for.
        /// </summary>
        /// <param name="regions"></param>
        /// <returns></returns>
        public TBuilder Regions(params Region[] regions)
        {
            _regions = new List<Region>(regions);
            return this as TBuilder;
        }

        #region Hidden

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public new Type GetType()
        {
            return this.GetType();
        }

        [EditorBrowsable(EditorBrowsableState.Never)]
        public override string ToString()
        {
            return base.ToString();
        }

        #endregion Hidden
    }
}